﻿using System;
using Scribe4net;
using ApprovalTests;
using ApprovalTests.Reporters;
using System.Collections.Generic;
using NUnit.Framework;
using System.Xml;
using System.Xml.Linq;
using System.Web.Script.Serialization;
using System.Reflection;

namespace Scribe4net.Tests
{
    [TestFixture]
    [UseReporter(typeof(BeyondCompareReporter))]
    public class TestSerialization
    {
        NodeForTesting structure1;
        List<object> list;
        List<object> listWithDict;
        Dictionary<string, object> dict;
        Dictionary<NodeForTesting, object> dictWithObjKey;
        string primitive1 = "stam1";
        string primitive2 = "sta'\"{}<>m1";
        int primitive3 = 123;
        DateTime primitive4 = new DateTime(2013, 2, 5);

        [SetUp]
        public void TestInitialize()
        {
            var dummyDate = DateTime.Parse("12/05/2012 12:09:22");

            structure1 = new NodeForTesting("123", 12, dummyDate);

            var c1 = new NodeForTesting("c1", 12, dummyDate);
            var c2 = new NodeForTesting("c2", 12, dummyDate);
            var c3 = new NodeForTesting("c3", 12, dummyDate);
            var c4 = new NodeForTesting("c4", 12, dummyDate);
            c4.Children = null;
            var c11 = new NodeForTesting("c11", 12, dummyDate);
            var c12 = new NodeForTesting("c12", 12, dummyDate);
            var c21 = new NodeForTesting("c21", 12, dummyDate);
            var c211 = new NodeForTesting("c211", 12, dummyDate);
            var c212 = new NodeForTesting("c212", 12, dummyDate);
            var c2121 = new NodeForTesting("c2121", 12, dummyDate);
            var c21211 = new NodeForTesting("c21211", 12, dummyDate);

            c1.Children.Add(c11);
            c1.Children.Add(c12);
            c2.Children.Add(c21);
            c21.Children.Add(c211);
            c21.Children.Add(c212);
            c212.Children.Add(c2121);
            c212.Children.Add(null);
            c2121.Children.Add(c21211);
            structure1.Children.Add(c1);
            structure1.Children.Add(c2);
            structure1.Children.Add(c3);
            structure1.Children.Add(c4);

            //list as first object
            list = new List<object>();
            list.Add(new NodeForTesting("c1", 1, dummyDate));
            list.Add(new NodeForTesting("c2", 2, dummyDate));
            list.Add(new NodeForTesting("c3", 3, dummyDate));

            //array as first object
            var arr = list.ToArray();

            //dict as first object
            dict = new Dictionary<string, object>();
            dict.Add("stam", "stamVal");
            dict.Add("stam1", 123);
            dict.Add("stam2", dummyDate);
            dict.Add("stam3", c1);

            dictWithObjKey = new Dictionary<NodeForTesting, object>();
            dictWithObjKey.Add(c1, dummyDate);

            //dictionary inside the structure
            listWithDict = new List<object>();
            listWithDict.Add(new NodeForTesting("c1", 1, dummyDate));
            listWithDict.Add("primitive1");
            listWithDict.Add(new NodeForTesting("c2", 2, dummyDate));
            listWithDict.Add(dict);
            listWithDict.Add("primitive2");
            listWithDict.Add(new NodeForTesting("c3", 3, dummyDate));
            listWithDict.Add(22);
            listWithDict.Add(dummyDate);
        }

        public string VerifyObjectXml(object obj, int dept = -1)
        {
            string result = TestObjectSerializer.SerializeToString(obj, SerializationType.Xml, new SerializationSettings() { MaxDept = dept });
            Approvals.Verify(result);
            try
            {
                XElement.Parse(result);
            }
            catch
            {
                NUnit.Framework.Assert.Fail();
            }
            return result;
        }

        public void VerifyObjectSerialization(object obj, SerializationType serType, SerializationSettings settings = null)
        {
            string result = TestObjectSerializer.SerializeToString(obj, serType, settings);

            Approvals.Verify(result);

            switch (serType)
            {
                case SerializationType.Xml:
                case SerializationType.XmlNoListTags:
                    try
                    {
                        XElement.Parse(result);
                    }
                    catch
                    {
                        NUnit.Framework.Assert.Fail();
                    }
                    break;

                case SerializationType.Json:
                    try
                    {
                        JavaScriptSerializer json_serializer = new JavaScriptSerializer();
                        object routes_list = json_serializer.DeserializeObject(result);
                    }
                    catch
                    {
                        NUnit.Framework.Assert.Fail();
                    }

                    break;
            }

        }

        public string VerifyObjectJson(object obj, int dept = -1)
        {
            string result = TestObjectSerializer.SerializeToString(obj, SerializationType.Json, new SerializationSettings() { MaxDept = dept });
            Approvals.Verify(result);
            try
            {
                JavaScriptSerializer json_serializer = new JavaScriptSerializer();
                object routes_list = json_serializer.DeserializeObject(result);
            }
            catch
            {
                NUnit.Framework.Assert.Fail();
            }
            return result;
        }

        [Test]
        public void TestXmlStringPrimitive()
        {
            string result = TestObjectSerializer.SerializeToString(primitive1, SerializationType.Xml);
            Approvals.Verify(result);
        }

        [Test]
        public void TestXmlStringPrimitiveWithBadChars()
        {
            string result = TestObjectSerializer.SerializeToString(primitive2, SerializationType.Xml);
            Approvals.Verify(result);
        }

        [Test]
        public void TestXmlIntPrimitive()
        {
            string result = TestObjectSerializer.SerializeToString(primitive3, SerializationType.Xml);
            Approvals.Verify(result);
        }

        [Test]
        public void TestXmlDatePrimitive()
        {
            string result = TestObjectSerializer.SerializeToString(primitive4, SerializationType.Xml);
            Approvals.Verify(result);
        }

        [Test]
        public void TestJsonStringPrimitive()
        {
            string result = TestObjectSerializer.SerializeToString(primitive1, SerializationType.Json);
            Approvals.Verify(result);
        }

        [Test]
        public void TestJsonStringPrimitiveWithBadChars()
        {
            string result = TestObjectSerializer.SerializeToString(primitive2, SerializationType.Json);
            Approvals.Verify(result);
        }

        [Test]
        public void TestJsonIntPrimitive()
        {
            string result = TestObjectSerializer.SerializeToString(primitive3, SerializationType.Json);
            Approvals.Verify(result);
        }

        [Test]
        public void TestJsonDatePrimitive()
        {
            string result = TestObjectSerializer.SerializeToString(primitive4, SerializationType.Json);
            Approvals.Verify(result);
        }

        [Test]
        public void TestXmlListWithDict()
        {
            VerifyObjectXml(listWithDict);
        }

        [Test]
        public void TestJsonListWithDict()
        {
            VerifyObjectJson(listWithDict);
        }

        [Test]

        public void TestXmlArrayFirst()
        {
            VerifyObjectXml(list.ToArray());
        }

        [Test]
        public void TestJsonArrayFirst()
        {
            VerifyObjectJson(list.ToArray());
        }


        [Test]
        public void TestXmlListFirst()
        {
            VerifyObjectXml(list);
        }

        [Test]
        public void TestJsonListFirst()
        {
            VerifyObjectJson(list);
        }

        [Test]
        public void TestXmlAllLevels()
        {
            VerifyObjectXml(structure1);
        }

        [Test]
        public void TestJsonAllLevels()
        {
            VerifyObjectJson(structure1);
        }

        [Test]
        public void TestJson2Levels()
        {
            VerifyObjectJson(structure1, 2);
        }

        [Test]
        public void TestXml2Levels()
        {
            VerifyObjectXml(structure1, 2);
        }

        [Test]
        public void TestJson3Levels()
        {
            VerifyObjectJson(structure1, 3);
        }

        [Test]
        public void TestXml3Levels()
        {
            VerifyObjectXml(structure1, 3);
        }

        [Test]
        public void TestJson4Levels()
        {
            VerifyObjectJson(structure1, 4);
        }

        [Test]
        public void TestXml4Levels()
        {
            VerifyObjectXml(structure1, 4);
        }

        [Test]
        public void TestJson5Levels()
        {
            VerifyObjectJson(structure1, 5);
        }

        [Test]
        public void TestXml5Levels()
        {
            VerifyObjectXml(structure1, 5);
        }

        [Test]
        public void TestXmlDictionaryWithObjKey()
        {
            VerifyObjectXml(dictWithObjKey);
        }

        [Test]
        public void TestJsonDictionaryWithObjKey()
        {
            VerifyObjectJson(dictWithObjKey);
        }

        [Test]
        public void TestJsonPrivateFields()
        {
            SerializationSettings settings = new SerializationSettings() { MaxDept = -1, IncludeFields = true, FieldInclusion = BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic };
            VerifyObjectSerialization(structure1, SerializationType.Json, settings);
        }

        [Test]
        public void TestJsonPrivateProperties()
        {
            SerializationSettings settings = new SerializationSettings() { MaxDept = -1, IncludeFields = false, PropertyInclusion = BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic };
            VerifyObjectSerialization(structure1, SerializationType.Json, settings);
        }

        [Test]
        public void TestJsonFields()
        {
            SerializationSettings settings = new SerializationSettings() { MaxDept = -1, IncludeFields = true, FieldInclusion = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic };
            VerifyObjectSerialization(structure1, SerializationType.Json, settings);
        }

        [Test]
        public void TestXmlPrivateFields()
        {
            SerializationSettings settings = new SerializationSettings() { MaxDept = -1, IncludeFields = true, FieldInclusion = BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic };
            VerifyObjectSerialization(structure1, SerializationType.Xml, settings);
        }

        [Test]
        public void TestXmlOnlyPrivateProperties()
        {
            SerializationSettings settings = new SerializationSettings() { MaxDept = -1, IncludeFields = false, PropertyInclusion = BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic };
            VerifyObjectSerialization(structure1, SerializationType.Xml, settings);
        }

        [Test]
        public void TestXmlFields()
        {
            SerializationSettings settings = new SerializationSettings() { MaxDept = -1, IncludeFields = true, FieldInclusion = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic };
            VerifyObjectSerialization(structure1, SerializationType.Xml, settings);
        }

        [TearDown]
        public void TestCleanup()
        {
        }
    }
}
